package com.daipresents.gfmon;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.sun.management.GcInfo;

public class GCMonitor implements GCMonitorMBean {

    private static final String SUCCESS = "success";
    
    private static final String GC_COPY = "Copy";
    private static final String GC_MARK_SWEEP_COMPACT = "MarkSweepCompact";
    
    private Logger logger = Logger.getLogger(GCMonitor.class);
    private List<com.sun.management.GarbageCollectorMXBean> sunBeans = 
        new ArrayList<com.sun.management.GarbageCollectorMXBean>();
    private boolean isSunBean = false;
    
    public GCMonitor() {
        super();
        logger.debug(GCMonitor.class.getName() + "Constractor Called.");
        
        List<GarbageCollectorMXBean> beans = 
            ManagementFactory.getGarbageCollectorMXBeans();
        
        if (beans != null){
            for(GarbageCollectorMXBean bean : beans){
                if(bean instanceof com.sun.management.GarbageCollectorMXBean){
                    sunBeans.add((com.sun.management.GarbageCollectorMXBean)bean);
                    isSunBean = true;
                }
            }
        }
    }
    
    @Override
    public String getLogging(){
        String message = 
            String.format("CopyDuration:%s,MarkSweepCompactDuration:%s,CopyGCMemoryCutDown:%s,MarkSweepCompactCutDownInPercent:%s",
                    getCopyDuration(),
                    getMarkSweepCompactDuration(),
                    getCopyGCMemoryCutDownInPercent(),
                    getMarkSweepCompactCutDownInPercent());
        logger.info(message);
        return SUCCESS;
    }
    
    @Override
    public long getCopyDuration() {
        return getDurationByGCName(GC_COPY);
    }

    @Override
    public long getMarkSweepCompactDuration() {
        return getDurationByGCName(GC_MARK_SWEEP_COMPACT);
    }
    
    @Override
    public int getCopyGCMemoryCutDownInPercent() {
        return getGCMemoryCutDownInPercentByName(GC_COPY);
    }

    @Override
    public int getMarkSweepCompactCutDownInPercent() {
        return getGCMemoryCutDownInPercentByName(GC_MARK_SWEEP_COMPACT);
    }
    
    @Override
    public boolean isSunBean(){
        return this.isSunBean;
    }
    
    private long getDurationByGCName(String gcName){
        
        if(sunBeans.size() == 0 || !isSunBean()){
            return 0;
        }
        
        for(com.sun.management.GarbageCollectorMXBean sunBean : sunBeans){
            if(sunBean.getName().equals(gcName)){
                GcInfo info = sunBean.getLastGcInfo();
                return info.getDuration();
            }
        }
        
        return 0;
    }
    
    private int getGCMemoryCutDownInPercentByName(String gcName){
        
        if(sunBeans.size() == 0 || !isSunBean()){
            return 0;
        }
        
        for(com.sun.management.GarbageCollectorMXBean sunBean : sunBeans){
            
            if(sunBean.getName().equals(gcName)){
                
                GcInfo info = sunBean.getLastGcInfo();
                
                long beforeUsage = 0;
                long beforeMax = 0;
                
                Map<String, MemoryUsage> beforeMap = info.getMemoryUsageBeforeGc();
                for(MemoryUsage usage : beforeMap.values()){
                    beforeUsage += usage.getUsed();
                    beforeMax += usage.getMax();
                }
                
                int beforePercent = (int) (100 * beforeUsage / beforeMax);
                
                long afterUsage = 0;
                long afterMax = 0;
                
                Map<String, MemoryUsage> afterMap = info.getMemoryUsageAfterGc();
                for(MemoryUsage usage : afterMap.values()){
                    afterUsage += usage.getUsed();
                    afterMax += usage.getMax();
                }
                
                int afterPercent = (int) (100 * afterUsage / afterMax);
                
                return afterPercent - beforePercent;
            }
        }
        
        return 0;
    }
}
